package device

import (
	"micro-rulego/internal/biz/hardware"
	"sync"

	"github.com/google/uuid"
)

type deviceHandle struct {
	data    []byte
	ischeck bool
	connc   connectController
}

type deviceManagerUsecase struct {
	hwctrl hardware.Controller
	dtable map[DeviceID]*deviceHandle
	m      sync.RWMutex
}

func (d *deviceManagerUsecase) DeviceCreate(deviceSpec []byte) error {
	err := d.hwctrl.DeviceConnect(deviceSpec)
	if err != nil {
		return err
	}

	return nil
}

func (d *deviceManagerUsecase) DeviceDestroy(devID DeviceID) error {
	d.m.RLock()
	defer d.m.RUnlock()
	handle, exist := d.dtable[devID]
	if !exist {
		return ErrDeviceHandleNotFound
	}
	err := d.hwctrl.DeviceDisconnect(handle.data)
	if err != nil {
		return err
	}
	return nil
}

func (d *deviceManagerUsecase) DeivceCheck() (map[DeviceID]DeviceData, error) {
	devHandles, err := d.hwctrl.DeviceHandles()
	if err != nil {
		return nil, err
	}
	d.m.Lock()
	defer d.m.Unlock()
	for _, v := range d.dtable {
		v.ischeck = false
	}
	ddata := make(map[DeviceID]DeviceData)
	for _, v := range devHandles {
		devID := uuid.NewSHA1(uuid.NameSpaceURL, v).ID()
		ddata[DeviceID(devID)] = DeviceData(v)
		if handle, exist := d.dtable[DeviceID(devID)]; exist {
			handle.ischeck = true
			continue
		}
		d.dtable[DeviceID(devID)] = &deviceHandle{
			data:    v,
			ischeck: true,
			connc:   newConnectController(v, d.hwctrl),
		}
	}
	for id, v := range d.dtable {
		if !v.ischeck {
			delete(d.dtable, id)
		}
	}
	return ddata, nil
}

func (d *deviceManagerUsecase) DeviceConnectCreate(devID DeviceID, connData []byte) error {
	d.m.RLock()
	defer d.m.RUnlock()
	handle, exist := d.dtable[devID]
	if !exist {
		return ErrDeviceHandleNotFound
	}
	return handle.connc.ConnectCreate(connData)
}

func (d *deviceManagerUsecase) DeviceConnectDestroy(devID DeviceID, connID ConnectID) error {
	d.m.RLock()
	defer d.m.RUnlock()
	handle, exist := d.dtable[devID]
	if !exist {
		return ErrDeviceHandleNotFound
	}
	return handle.connc.ConnectDestroy(connID)
}

func (d *deviceManagerUsecase) DeviceConnectCheck(devID DeviceID) (map[ConnectID]*ConnectStatus, error) {
	d.m.RLock()
	defer d.m.RUnlock()
	handle, exist := d.dtable[devID]
	if !exist {
		return nil, ErrDeviceHandleNotFound
	}
	return handle.connc.ConnectCheck()
}

func (d *deviceManagerUsecase) DeviceConnectTransimit(devID DeviceID, connID ConnectID, data []byte) error {
	d.m.RLock()
	defer d.m.RUnlock()
	handle, exist := d.dtable[devID]
	if !exist {
		return ErrDeviceHandleNotFound
	}
	return handle.connc.ConnectTransimit(connID, data)
}

func (d *deviceManagerUsecase) DeviceConnectReceive(devID DeviceID, connID ConnectID) ([]byte, error) {
	d.m.RLock()
	defer d.m.RUnlock()
	handle, exist := d.dtable[devID]
	if !exist {
		return nil, ErrDeviceHandleNotFound
	}
	return handle.connc.ConnectReceive(connID)
}

func NewDeviceManager(h hardware.Controller) DeviceManager {
	return &deviceManagerUsecase{
		hwctrl: h,
		dtable: make(map[DeviceID]*deviceHandle),
	}
}
